#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/norm.hpp>
#include <cstdio>

void
calc_distance_from_camera() {
	glm::vec4 somepoint(5.0f, 5.0f, 5.0f, 1.0f);
	glm::vec3 camerapos(2.0f, 2.0f, 2.0f);
	glm::mat4 viewmat = glm::lookAt(camerapos, glm::vec3(0, 0, 0), glm::vec3(0, 1.0f, 0));
	glm::vec4 somepoint_view = viewmat * somepoint; // transform somepoint to view space

	// 当只有 view matrix 时，也可以计算出摄像机的位置，下面代码就展示了这种计算方式，
	// 至于原理可以先忽略，涉及到数学的部分总是让人害怕。
	glm::mat4 viewmat_inverse = glm::inverse(viewmat);
	glm::vec3 camerapos_calc(viewmat_inverse[3]);
	printf("here camera pos: %.2f %.2f, %.2f\n", camerapos.x, camerapos.y, camerapos.z);
	printf("calc camera pos: %.2f %.2f, %.2f\n", camerapos_calc.x, camerapos_calc.y, camerapos_calc.z);

	// 计算点 somepoint 距离摄像机的距离
	// 方式 1 ，在 world space 中计算距离
	float distance_world = glm::distance2(glm::vec3(somepoint), camerapos_calc);
	printf("distance calc in world space:%.2f\n", distance_world);

	// 方式 2 ，在 view space 中计算距离，
	// view space 就是在摄像机位置中观看对象，此时摄像机就相当于原点
	glm::vec3 camerapos_view(0, 0, 0);
	float distance_view = glm::distance2(glm::vec3(somepoint_view), camerapos_view);
	printf("distance calc in view space:%.2f\n", distance_view);
	printf("see that, distance_world == distance_view\n");
}

void 
calc_near_far() {
	// 采用右手坐标系验证
	float neardistance = 1.0f;
	float fardistance = 100.0f;
	glm::mat4 persmat = glm::perspectiveRH(glm::radians(45.0f), 800.0f/600.0f, neardistance, fardistance);
	// NDC 是基于左手坐标系的，近平面对应的 NDC 坐标的 z 轴的值是 -1.0f ，
	// 而远平面对应的 NDC 坐标的 z 轴的值是 1.0f 。
	glm::vec4 near_ndc(0, 0, -1.0f, 1.0f);
	glm::vec4 far_ndc(0, 0, 1.0f, 1.0f);

	// 由于我们逆运算这个 ndc 坐标之前所在的世界坐标位置，所以我们要先求逆矩阵。
	glm::mat4 inverse_permat = glm::inverse(persmat);

	glm::vec4 near_world = inverse_permat * near_ndc;
	glm::vec4 far_world = inverse_permat * far_ndc;
	printf("before /w: \tnear_world:<%9.3f,%9.3f,%9.3f,%9.3f>\n\t\tfar_world: <%9.3f,%9.3f,%9.3f,%9.3f>\n", 
		near_world.x, near_world.y, near_world.z, near_world.w, 
		far_world.x, far_world.y, far_world.z, far_world.w);
	
	// 我们知道 OpenGL 会自动进行透视除法（/w）来将透视矩阵转换后的坐标最终转换成 NDC 。
	// 而刚刚乘以逆矩阵只消除了透视投影，未消除透视除法，
	// 这时还应该再除以 w 分量，让 w 分量为 1 来消除透视除法的影响。
	near_world /= near_world.w;
	far_world /= far_world.w;
	printf("after /w: \tnear_world:<%9.3f,%9.3f,%9.3f,%9.3f>\n\t\tfar_world: <%9.3f,%9.3f,%9.3f,%9.3f>\n",
		near_world.x, near_world.y, near_world.z, near_world.w, 
		far_world.x, far_world.y, far_world.z, far_world.w);
	printf("see, in right-hand the z axis of result match -neardistance and -fardistance\n");
}

int 
main() {
	calc_distance_from_camera();
	printf("\n\n");
	calc_near_far();
	return 0;
}
